<?php

namespace App\Kuafu\Shop\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Base\Enum\BaseCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\Shop\Model\GuanyuanSyncShopListModel;
use App\Kuafu\Shop\Model\ShopStaffRecordLogModel;
use App\Kuafu\Shop\Model\ShopStaffRecordModel;
use App\Kuafu\User\Enum\RoleCodeEnum;
use App\Kuafu\User\Model\User;
use App\Kuafu\User\Model\UserRoleMapping;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use App\Kuafu\Shop\Enum\CodeEnum;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Shop\Model\ShopRecordsModel;
use Carbon\Carbon;

class ShopStaffRecordService
{

    /**
     * 获得审核详情
     * @param int $userId
     * @param int $shopId
     * @return array
     * @throws ShopException
     */
    public static function info(int $userId, int $shopId): array
    {
        $recordInfo = ShopStaffRecordModel::query()
            ->where('userId', $userId)
            ->where('shopId', $shopId)
            ->with([
                'userRel' => function (HasOne $query) {
                    $query->with(['userIdCardRel']);
                },
                'shopRel'
            ])
            ->first();

        if ($recordInfo == null) {
            throw new ShopException(400, '该记录不存在');
        }

        // 获取身份证
        $healthCardStartDate = '';
        $healthCardEndDate   = '';
        $healthCardPhoto     = '';
        $idCardPhoto         = '';
        if ($recordInfo->userRel) {
            $healthCardStartDate = $recordInfo->userRel->healthCardEndDate ? Carbon::createFromTimestamp($recordInfo->userRel->healthCardEndDate)->subYear()->toDateString() : '';
            $healthCardEndDate   = $recordInfo->userRel->healthCardEndDate ? Carbon::createFromTimestamp($recordInfo->userRel->healthCardEndDate)->toDateString() : '';
            $healthCardPhoto     = strlen($recordInfo->userRel->healthCardPhoto) ? tigerPicturePath($recordInfo->userRel->healthCardPhoto) : '';
            if ($recordInfo->userRel->userIdCardRel) {
                $idCardPhoto = privateFilePath($recordInfo->userRel->userIdCardRel->facePhoto);
            }
        }

        return [
            'id'                  => $recordInfo->id,
            'userId'              => $recordInfo->userId,
            'shopCode'            => (string)optional($recordInfo->shopRel)->shopCode,
            'shopName'            => (string)optional($recordInfo->shopRel)->name,
            'username'            => $recordInfo->userRel->username ?? '',
            'healthCardStartDate' => $healthCardStartDate,
            'healthCardEndDate'   => $healthCardEndDate,
            'healthCardPhoto'     => $healthCardPhoto,
            'idCardPhoto'         => $idCardPhoto,
            'auditStatus'         => $recordInfo->auditStatus,
            'auditStatusText'     => $recordInfo->auditStatusText,
            'causeOfRejection'    => $recordInfo->causeOfRejection,
            'nextAuditInfo'       => self::getNextAuditStaff($shopId, $userId)
        ];
    }

    /**
     * @param int $userId
     * @param int $healthCardStartDate
     * @param int $optUserId
     * @return array
     * @throws BaseException
     */
    public static function editHealthCardDate(int $userId, string $healthCardStartDate, int $optUserId)
    {
        $healthCardStartDateTimestamp = Carbon::parse($healthCardStartDate)->startOfDay()->timestamp;

        if ($healthCardStartDateTimestamp > time()){
            throw new BaseException(BaseCodeEnum::ERR_400000, '修改时间必须小于当前时间');
        }

        $healthCardEndDateTimestamp = strtotime('+1 year', $healthCardStartDateTimestamp);

        $res = User::query()->where('id', $userId)->update([
            'healthCardEndDate' => $healthCardEndDateTimestamp,
            'updatedAt'         => time(),
            'updatedByUid'      => $optUserId
        ]);

        if (empty($res)){
            throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR,'更新异常');
        }
        $shops = ShopStaffRecordModel::query()
                                     ->where('userId', $userId)
                                     ->get();

        $addLogs = [];
        foreach ($shops as $shop) {
            $addLogs[] = [
                'shopId'       => $shop->shopId,
                'shopCode'     => $shop->shopCode,
                'userId'       => $userId,
                'operateType'  => ShopStaffRecordLogModel::OPERATE_TYPE_INNER,
                'actionType'   => ShopStaffRecordLogModel::ACTION_TYPE_CARD_CHANGE_DATE,
                'remark'       => '',
                'createdByUid' => $optUserId,
                'updatedByUid' => $optUserId,
                'createdAt'    => time(),
                'updatedAt'    => time(),
            ];
        }
        ShopStaffRecordLogService::batchAddRecord($addLogs);

        Artisan::call('shop:refresh_shop_staff_statistics --userIds=' . $userId);

        return [
            'healthCardStartDate' => Carbon::createFromTimestamp($healthCardStartDateTimestamp)->toDateString(),
            'healthCardEndDate'   => Carbon::createFromTimestamp($healthCardEndDateTimestamp)->toDateString(),
            'auditPermission' => ShopStaffRecordModel::healthCardStatus(
                    $healthCardStartDateTimestamp
                ) == ShopStaffRecordModel::HEALTH_CARD_STATUS_EXPIRED
        ];
    }

    /**
     * @param int $userId
     * @param int $shopId
     * @param int $optUserId
     * @param int $auditStatus
     * @param string $remark
     * @throws \Exception
     */
    public static function submit(int $userId, int $shopId, int $optUserId, int $auditStatus, string $remark = '')
    {
        $user = User::query()->where('id', $userId)->first();
        if (empty($user->healthCardEndDate)) {
            throw new BaseException(BaseCodeEnum::ERR_400000, '健康证时间未填写');
        }

        if ($user->healthCardEndDate < time() && $auditStatus == ShopStaffRecordModel::AUDIT_ACTION_APPROVED) {
            throw new BaseException(BaseCodeEnum::ERR_400000, '健康证过期不可通过');
        }

        $staffs        = ShopStaffRecordModel::query()->where('userId', $userId)->get();
        $inShopRecords = $notInShopRecords = [];
        foreach ($staffs as $staff) {
            if ($staff->inShop == ShopStaffRecordModel::IN_SHOP_ON) {
                $inShopRecords[] = $staff;
            }
            if ($staff->inShop == ShopStaffRecordModel::IN_SHOP_OFF) {
                $notInShopRecords[] = $staff;
            }
        }

        DB::beginTransaction();
        try {
            /** @var Collection $inShopRecordsChunk */
            foreach (collect($inShopRecords)->chunk(100) as $inShopRecordsChunk) {
                ShopStaffRecordModel::query()
                                    ->whereIn('id', $inShopRecordsChunk->pluck('id')->toArray())
                                    ->update([
                                        'healthCardStatus' => ShopStaffRecordModel::healthCardStatus(
                                            $user->healthCardEndDate
                                        ),
                                        'auditStatus'      => ShopStaffRecordModel::AUDIT_ACTION_STATUS_MAP[$auditStatus],
                                        'causeOfRejection' => ShopStaffRecordModel::AUDIT_ACTION_REJECT === $auditStatus
                                            ? $remark
                                            : '',
                                        'updatedByUid'     => $optUserId,
                                        'updatedAt'        => time(),
                                        'lastAuditTime'    => time(),
                                    ]);
                ShopRecordsModel::query()
                                ->whereIn('shopId', $inShopRecordsChunk->pluck('shopId')->toArray())
                                ->update([
                                    'lastAuditTime' => time(),
                                    'updatedByUid'  => $optUserId,
                                    'updatedAt'     => time()
                                ]);

                foreach ($inShopRecordsChunk as $inShopRecord) {
                    ShopStaffRecordLogService::addRecord(
                        $inShopRecord->shopId, $inShopRecord->shopCode, $userId, $optUserId,
                        ShopStaffRecordLogModel::OPERATE_TYPE_INNER,
                        ShopStaffRecordModel::AUDIT_ACTION_APPROVED == $auditStatus
                            ? ShopStaffRecordLogModel::ACTION_TYPE_AUDIT_APPROVED
                            : ShopStaffRecordLogModel::ACTION_TYPE_AUDIT_REJECT,
                        $remark
                    );
                }
            }
            /** @var Collection $inShopRecordsChunk */
            foreach (collect($notInShopRecords)->chunk(100) as $notInShopRecordsChunk) {
                ShopStaffRecordModel::query()
                                    ->whereIn('id', $notInShopRecordsChunk->pluck('id')->toArray())
                                    ->update(
                                        [
                                            'healthCardStatus' => ShopStaffRecordModel::healthCardStatus(
                                                $user->healthCardEndDate
                                            ),
                                            'updatedByUid'     => $optUserId,
                                            'updatedAt'        => time(),
                                        ]
                                    );
            }
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR, '更新异常:' . $exception->getMessage());
        }
        //更新shopRecord
        Artisan::call('shop:refresh_shop_staff_statistics --userIds=' . $userId);
    }

    /**
     * 员工备案列表
     * @param int $pageNum
     * @param int $pageSize
     * @param string $username
     * @param string $positionStatus
     * @param string $inShop
     * @param string $auditStatus
     * @param string $auditStartDate
     * @param string $auditEndDate
     * @param string $updateStartDate
     * @param string $updateEndDate
     * @param string $shopIds
     * @param string $dimCode
     * @return array
     */
    public static function list(
        int    $pageNum,
        int    $pageSize,
        string $username,
        string $positionStatus,
        string $inShop,
        string $auditStatus,
        string $auditStartDate,
        string $auditEndDate,
        string $updateStartDate,
        string $updateEndDate,
        string $shopIds,
        string $dimCode,
        string $sortField,
        string $sortOrder,
        bool $export = false
    ): array {

        $modelQuery = ShopStaffRecordModel::query()
            ->when(strlen($username), function ($query) use ($username) {
                $query->join('users', 'shop_staff_records.userId', '=', 'users.id')
                    ->where(function ($q) use ($username) {
                        $q->where('users.username', 'like', '%' . $username . '%')
                            ->orWhere('users.mobile', $username);
                    });
            })
            ->when(strlen($dimCode) && !is_numeric($dimCode), function ($query) use ($dimCode) {
                $query->join('shops', 'shop_staff_records.shopId', '=', 'shops.id')
                    ->where('shops.name', 'like', '%' . $dimCode . '%');
            })
            ->where(function ($query) use ($dimCode) {
                $query->when(strlen($dimCode) && is_numeric($dimCode), function ($query) use ($dimCode) {
                    $query->where('shopCode', $dimCode);
                });
            })
            ->when(strlen($shopIds), function ($query) use ($shopIds) {
                $shopIds = explode(',', $shopIds);
                $query->whereIn('shopId', $shopIds);
            })
            ->when(strlen($positionStatus), function ($query) use ($positionStatus) {
                $query->where('positionStatus', $positionStatus);
            })
            ->when(strlen($inShop), function ($query) use ($inShop) {
                $query->whereIn('inShop', explode(',', $inShop));
            })
            ->when(strlen($auditStatus), function ($query) use ($auditStatus) {
                $query->whereIn('auditStatus', explode(',', $auditStatus));
            })
            ->when(strlen($auditStartDate) && strlen($auditEndDate), function ($query) use ($auditStartDate, $auditEndDate) {
                $query->whereBetween('lastAuditTime', [Carbon::parse($auditStartDate)->startOfDay()->timestamp, Carbon::parse($auditEndDate)->endOfDay()->timestamp]);
            })
            ->when(strlen($updateStartDate) && strlen($updateEndDate), function ($query) use ($updateStartDate, $updateEndDate) {
                $query->whereBetween('lastManagerUpdateTime', [Carbon::parse($updateStartDate)->startOfDay()->timestamp, Carbon::parse($updateEndDate)->endOfDay()->timestamp]);
            })
            ->when(in_array($sortField, ['shopCode', 'lastAuditTime', 'lastManagerUpdateTime']) && strlen($sortOrder), function ($query) use ($sortField, $sortOrder) {
                switch ($sortOrder) {
                    case "ascend":
                        $query->orderBy($sortField);
                        break;
                    case "descend":
                        $query->orderByDesc($sortField);
                        break;
                }
            })
            ->orderByDesc('lastManagerUpdateTime')
            ->with(['userRel', 'shopRel']);

        $paginator = null;
        if ($export){
            $list = [];
            $modelQuery->chunk(500, function ($shopStaffLists) use (&$list) {
                $list = array_merge($list, self::getShopStaffList(collect($shopStaffLists)));
            });
        } else {
            $paginator = $modelQuery->paginate($pageSize, ['*'], 'pageNum', $pageNum);
            $collectList = collect($paginator->items());
            $list = self::getShopStaffList($collectList);
        }

        return [
            'list'     => $list,
            'pageNum'  => $pageNum,
            'pageSize' => $pageSize,
            'count'    => $paginator ? $paginator->total() : null,
        ];
    }


    /**
     * 多次查询
     * @param $collectList
     * @return mixed
     */
    public static function getShopStaffList($collectList)
    {
        $shopIds = $collectList->pluck('shopId')->unique()->values()->toArray();
        $userIds = $collectList->pluck('userId')->unique()->values()->toArray();
        $shopCode = $collectList->pluck('shopCode')->unique()->values()->toArray();

        // 获取门店状态
        $shopStatus = GuanyuanSyncShopListModel::query()
            ->whereIn('code', $shopCode)
            ->pluck('status', 'code')
            ->toArray();

        // 获取门店到店员工数量
        $inShopList = ShopRecordsModel::query()
            ->whereIn('shopId', $shopIds)
            ->pluck('inShopStaffCount', 'shopId');

        // 获得督导
        $supervisorIds = Shop::query()
            ->whereIn('id', $shopIds)
            ->pluck('councilorId', 'id')
            ->toArray();

        $supervisorUserList = [];
        User::query()
            ->whereIn('id', array_values($supervisorIds))
            ->select(['username', 'id'])
            ->get()
            ->map(function (User $user) use ($supervisorIds, &$supervisorUserList) {
                $supervisorUserList[array_search($user->id, $supervisorIds)] = $user->username;
            });

        // 获得职位信息
        $userPermission = [];

        UserRoleMapping::query()
            ->whereIn('shopId', $shopIds)
            ->whereIn('userId', $userIds)
            ->whereIn('type', [RoleCodeEnum::ROLE_POS_TYPE, RoleCodeEnum::ROLE_BUSINESS_TYPE])
            ->with(['rolesRel'])
            ->get()
            ->map(function (UserRoleMapping $userRoleMapping) use (&$userPermission) {
                if (strlen((string)optional($userRoleMapping->rolesRel)->name)) {
                    $userPermission[$userRoleMapping->userId . '-' . $userRoleMapping->shopId][] = $userRoleMapping->rolesRel->name;
                }
            });

        return $collectList->map(function (ShopStaffRecordModel $shopStaffRecordModel) use ($inShopList, $supervisorUserList, $userPermission, $shopStatus) {
            // 健康证到期时间
            $healthCardEndDate = '';
            if ($shopStaffRecordModel->userRel != null && $shopStaffRecordModel->userRel->healthCardEndDate) {
                $healthCardEndDate = Carbon::createFromTimestamp($shopStaffRecordModel->userRel->healthCardEndDate)->toDateString();
            }

            $auditPermission = in_array($shopStaffRecordModel->auditStatus, [ShopStaffRecordModel::AUDIT_STATUS_WAIT_AUDIT, ShopStaffRecordModel::AUDIT_STATUS_REJECT, ShopStaffRecordModel::AUDIT_STATUS_PASS]);

            // 省市区
            $province = '';
            $city     = '';
            $district = '';

            // 所属战区
            $warZone = '';

            if ($shopStaffRecordModel->shopRel != null) {
                $areaArr  = explode('-', $shopStaffRecordModel->shopRel->addressPrefix);
                $province = $areaArr[0] ?? '';
                $city     = $areaArr[1] ?? '';
                $district = $areaArr[2] ?? '';

                $warZone = $province
                    ? ShopRecordService::getWarZoneByProvinceCode((int)$shopStaffRecordModel->shopRel->province)
                    : '';
            }

            return [
                'id'                    => $shopStaffRecordModel->id,
                'userId'                => $shopStaffRecordModel->userId,
                'shopId'                => $shopStaffRecordModel->shopId,
                'username'              => (string)optional($shopStaffRecordModel->userRel)->username,
                'inShopText'            => $shopStaffRecordModel->inShopText,
                'auditStatusText'       => $shopStaffRecordModel->auditStatusText,
                'causeOfRejection'      => (string)$shopStaffRecordModel->causeOfRejection,
                'healthCardEndDate'     => $healthCardEndDate,
                'healthCardStatusText'  => $shopStaffRecordModel->healthCardStatusText,
                'idCardStatusText'      => $shopStaffRecordModel->idCardStatusText,
                'positionStatusText'    => $shopStaffRecordModel->positionStatusText,
                'permission'            => implode(',', array_unique($userPermission[$shopStaffRecordModel->userId . '-' . $shopStaffRecordModel->shopId] ?? [])),
                'lastAuditTime'         => $shopStaffRecordModel->lastAuditTime ? Carbon::createFromTimestamp($shopStaffRecordModel->lastAuditTime)->toDateTimeString() : '-',
                'lastManagerUpdateTime' => $shopStaffRecordModel->lastManagerUpdateTime ? Carbon::createFromTimestamp($shopStaffRecordModel->lastManagerUpdateTime)->toDateTimeString() : '-',
                'shopCode'              => $shopStaffRecordModel->shopCode,
                'shopName'              => (string)optional($shopStaffRecordModel->shopRel)->name,
                'shopStatusText'        => $shopStatus[$shopStaffRecordModel->shopCode] ?? '-',
                'councilorName'         => $supervisorUserList[$shopStaffRecordModel->shopId] ?? '-',
                'inShopStaffCount'      => $inShopList[$shopStaffRecordModel->shopId] ?? 0,
                'warZone'               => $warZone,
                'provinceName'          => $province,
                'cityName'              => $city,
                'districtName'          => $district,
                'auditPermission'       => $auditPermission,
            ];
        })->toArray();
    }


    /**
     * 获取下一个被审核人
     * @param int $shopId
     * @param int $userId
     * @return array
     */
    public static function getNextAuditStaff(int $shopId, int $userId): array
    {
        // 获得待审核的员工
        $shopStaffRecordModel = ShopStaffRecordModel::query()
            ->where('userId', '!=', $userId)
            ->where('auditStatus', ShopStaffRecordModel::AUDIT_STATUS_WAIT_AUDIT)
            ->orderByRaw("CASE WHEN shopId = {$shopId} THEN 0 ELSE 1 END, id DESC")
            ->select(['shopId', 'userId'])
            ->first();

        if (empty($shopStaffRecordModel)){
            return [];
        }

        return [
            'userId' => $shopStaffRecordModel->userId,
            'shopId' => $shopStaffRecordModel->shopId,
        ];
    }

    /**
     * @param int $shopId
     * @param array $userIds
     * @return array
     */
    public static function getShopStaffRecordList(int $shopId, array $userIds = [])
    {
        return ShopStaffRecordModel::query()
                                   ->where('shopId', $shopId)
                                   ->when($userIds, function ($query) use ($userIds) {
                                       $query->whereIn('userId', $userIds);
                                   })
                                   ->get()
                                   ->keyBy('userId')
                                   ->toArray();
    }


    /**
     * 获得某个员工历史审核状态
     * @param $userId
     * @return array
     */
    public static function getStaffHistoryAudit($userId): array
    {
        // 获得员工历史的审核状态
        $auditStatus      = ShopStaffRecordModel::AUDIT_STATUS_WAIT_AUDIT;
        $causeOfRejection = '';
        $auditedStatusModel = ShopStaffRecordModel::query()
            ->where('userId', $userId)
            ->whereIn('auditStatus', [ShopStaffRecordModel::AUDIT_STATUS_REJECT, ShopStaffRecordModel::AUDIT_STATUS_PASS])
            ->select(['auditStatus', 'causeOfRejection', 'healthCardStatus', 'idCardStatus'])
            ->first();

        if ($auditedStatusModel != null) {
            $auditStatus      = $auditedStatusModel->auditStatus;
            $causeOfRejection = $auditedStatusModel->causeOfRejection;
        }
        return compact('auditStatus', 'causeOfRejection');
    }
}